#! /usr/bin/env python
# -*- coding: utf8 -*-

import pygtk
pygtk.require('2.0')

import os
import gtk
import sys
import os.path
import gtk.gdk
import threading
from random import sample


def threaded(f):
	def wrapper(*args):
		t = threading.Thread(target=f, args=args)
		t.setDaemon(True)
		t.start()
	wrapper.__name__ = f.__name__
	wrapper.__dict__ = f.__dict__
	wrapper.__doc__  = f.__doc__
	return wrapper

if sys.version_info[:2] == (2, 5):
    from xml.etree import cElementTree as et
else:
    import cElementTree as et

class SampleMaker(gtk.Assistant):
    def __init__(self):
        gtk.Assistant.__init__(self)

        self.connect('prepare', self.cb_prepare)
        self.connect('delete_event', self.cb_on_delete)
        self.connect('close', self.cb_close)
        self.connect('cancel', self.cb_cancel)
        self.set_size_request(700, 490)
        self.set_forward_page_func(self.forward_page)

        self.__actions = {}
        self.__lock_xml = threading.Lock()

        self.__init_pg_0()
        self.__init_pg_1()
        self.__init_pg_1_5()
        self.__init_pg_2()
        self.__init_pg_3()

        l = gtk.Label("Thanks for using SampleMaker")
        l.show()
        self.append_page(l)
        self.set_page_title(l, "Finished!")
        self.set_page_type(l, gtk.ASSISTANT_PAGE_SUMMARY)
        self.__previous = -1
        self.__actions[l] = self.action_pg_5

    def forward_page(self, number):
        if number == 4 and self.pg3_btn_yes.get_active():
            return 3
        else:
            return number + 1

    def __init_pg_0(self):
        label = gtk.Label("Welcome to SampleMaker. This tool will sample elements from an XML file according to the configuration you input.")
        label.show()
        label.set_line_wrap(True)

        self.append_page(label)
        self.set_page_complete(label, True)
        self.set_page_title(label, "Welcome to SampleMaker")
        self.set_page_type(label, gtk.ASSISTANT_PAGE_INTRO)

    def __init_pg_1(self):
        self.spage = gtk.HBox()
        vbox = gtk.VBox()
        self.spage.pack_start(vbox, True, True, 5)
        #label = gtk.Label("Please select the XML file that you want to sample.")

        fileSelect = gtk.FileChooserWidget()
        filtro = gtk.FileFilter()
        filtro.add_pattern("*.xml")
        fileSelect.set_filter(filtro)
        fileSelect.connect("selection-changed", self.cb_selection_changed_pg1)

        #vbox.pack_start(label, False, False, 5)
        vbox.pack_start(fileSelect, True, True, 5)
        self.spage.show_all()

        self.append_page(self.spage)
        self.set_page_title(self.spage, "Select source XML File")
        self.set_page_type(self.spage, gtk.ASSISTANT_PAGE_CONTENT)
        self.set_page_complete(self.spage, False)


    def __init_pg_1_5(self):
        self.pg_1_5 = gtk.Label("Please wait while the XML file is parsed.")
        self.pg_1_5.show()

        self.__actions[self.pg_1_5] = self.action_pg_1_5
        self.append_page(self.pg_1_5)
        self.set_page_title(self.pg_1_5, "Parsing XML file")
        self.set_page_type(self.pg_1_5, gtk.ASSISTANT_PAGE_CONTENT)

    def __init_pg_2(self):
        self.pg2 = gtk.HBox()
        vbox = gtk.VBox()
        self.pg2.pack_start(vbox, False, False, 5)

        # First row
        self.pg2_hbox1 = gtk.HBox()
        bbox = gtk.HButtonBox()
        self.tag_name = gtk.Entry(128)
        self.pg2btn1 = btn1 = gtk.Button("Search", stock="gtk-find")

        btn1.connect("clicked", self.cb_clicked_btn1_pg2)
        btn1.connect("activate", self.cb_clicked_btn1_pg2)
        self.tag_name.connect("activate", self.cb_clicked_btn1_pg2)


        self.pg2_hbox1.pack_start(gtk.Label("Please insert the tag name:"), False, False, 5)
        self.pg2_hbox1.pack_start(self.tag_name, True, True, 5)
        self.pg2_hbox1.pack_start(bbox, False, False, 5)
        bbox.add(btn1)

        # Second row
        self.pg2_hbox2 = gtk.HBox()
        self.pg2_label1 = gtk.Label("How many elements (out of X) do you want to sample?")
        self.pg2_ammount_entry = gtk.Entry(15)
        self.pg2_ammount_entry.connect("changed", self.cb_pg2_ammount_changed)

        self.pg2_hbox2.pack_start(self.pg2_label1, False, False, 5)
        self.pg2_hbox2.pack_start(self.pg2_ammount_entry, False, False, 5)

        vbox.pack_start(self.pg2_hbox1, False, False, 5)
        vbox.pack_start(self.pg2_hbox2, False, False, 5)

        self.pg2.show_all()
        self.pg2_hbox2.hide_all()
        self.append_page(self.pg2)
        self.set_page_title(self.pg2, "Sampling configuration")
        self.set_page_type(self.pg2, gtk.ASSISTANT_PAGE_CONTENT)
        self.__actions[self.pg2] = self.action_pg_2

    def __init_pg_3(self):
        hbox = gtk.HBox()
        vbox = gtk.VBox()
        hbox.pack_start(vbox, False, False, 5)

        self.pg3_btn_yes = gtk.RadioButton(None, "Do another sampling from this file.")
        self.pg3_btn_no  = gtk.RadioButton(self.pg3_btn_yes, "Exit this program.")

        vbox.pack_start(self.pg3_btn_yes, False, False, 0)
        vbox.pack_start(self.pg3_btn_no, False, False, 0)

        hbox.show_all()
        self.__actions[hbox] = self.action_pg_3
        self.append_page(hbox)
        self.set_page_complete(hbox, False)
        self.set_page_title(hbox, "What would you like to do?")
        self.set_page_type(hbox, gtk.ASSISTANT_PAGE_CONTENT)


    # gtk.Assistant methods
    def cb_prepare(self, assistant, page):
        if page in self.__actions:
            self.__actions[page]()

        self.__previous = self.get_current_page()

    # "Actions" methods
    @threaded
    def action_pg_1_5(self):
        self.__lock_xml.acquire()
        self.__tree = et.parse(self.__fname).getroot()
        self.__lock_xml.release()

        gtk.gdk.threads_enter()
        self.set_page_complete(self.pg_1_5, True)
        self.set_current_page(self.get_current_page() + 1)
        gtk.gdk.threads_leave()

    def action_pg_2(self):
        self.pg2_hbox2.hide_all()
        self.pg2_ammount_entry.set_text("")
        self.tag_name.set_text("")

    def action_pg_3(self):
        buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_SAVE, gtk.RESPONSE_ACCEPT)
        dialog  = gtk.FileChooserDialog("Select destination XML file.", self, gtk.FILE_CHOOSER_ACTION_SAVE, buttons)

        filtro = gtk.FileFilter()
        filtro.add_pattern("*.xml")
        dialog.set_filter(filtro)
        dialog.set_property("do-overwrite-confirmation", True)

        if dialog.run() == gtk.RESPONSE_ACCEPT:
            fname = dialog.get_filename()
            root = et.Element(self.__tree.tag)
            map(root.append, sample(self.__nodes, self.__ammount))
            et.ElementTree(root).write(fname)
            self.set_page_complete(self.get_nth_page(4), True)
        else:
            pass

        dialog.destroy()

    def action_pg_5(self):
        gtk.main_quit()

    # Page 1 signal
    def cb_selection_changed_pg1(self, widget):
        fname = widget.get_filename()

        if fname != None and fname.lower().endswith(".xml") and os.path.isfile(fname):
            self.set_page_complete(self.spage, True)
            self.__fname = fname
        else:
            self.set_page_complete(self.spage, False)

    # Page 2 signals
    @threaded
    def cb_clicked_btn1_pg2(self, widget):
        gtk.gdk.threads_enter()
        self.pg2btn1.set_sensitive(False)
        self.tag_name.set_sensitive(False)
        gtk.gdk.threads_leave()

        tag = self.tag_name.get_text().strip()

        if tag == "":
            gtk.gdk.threads_enter()
            self.pg2btn1.set_sensitive(True)
            self.tag_name.set_sensitive(True)
            gtk.gdk.threads_leave()

            return

        self.__lock_xml.acquire()
        self.__nodes = self.__tree.findall(".//%s" % tag)
        ammount = len(self.__nodes)
        self.__lock_xml.release()

        gtk.gdk.threads_enter()
        if ammount >= 1:
            self.pg2_label1.set_text("How many elements (out of %d) do you want to sample?" % (ammount,))
            self.pg2_hbox2.show_all()
        else:
            self.pg2_hbox2.hide_all()

        self.pg2btn1.set_sensitive(True)
        self.tag_name.set_sensitive(True)
        gtk.gdk.threads_leave()

    def cb_pg2_ammount_changed(self, widget):
        if widget.get_text().isdigit() and int(widget.get_text()) <= len(self.__nodes) and int(widget.get_text()) > 0:
            self.__ammount = int(widget.get_text())
            self.set_page_complete(self.pg2, True)
        else:
            self.set_page_complete(self.pg2, False)

    # gtk.Assistant signals
    def cb_on_delete(self, widget, event):
        gtk.main_quit()

    def cb_cancel(self, assistant):
        if self.get_current_page() != 5:
            dialog = gtk.Dialog("Confirmation", self, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                                (gtk.STOCK_NO, gtk.RESPONSE_REJECT,
                                gtk.STOCK_YES, gtk.RESPONSE_ACCEPT))

            hbox = gtk.HBox()
            img = gtk.Image()
            img.set_from_stock(gtk.STOCK_DIALOG_QUESTION, gtk.ICON_SIZE_DIALOG)
            hbox.pack_start(img, False, False, 5)

            hbox.pack_start(gtk.Label("Do you want to quit SampleMaker?"), False, False, 5)

            hbox.show_all()
            dialog.vbox.pack_start(hbox)

            if dialog.run() == gtk.RESPONSE_ACCEPT:
                gtk.main_quit()

            dialog.destroy()

    def cb_close(self, assistant):
        self.emit('delete_event', gtk.gdk.Event(gtk.gdk.NOTHING))

if __name__ == '__main__':
    gtk.gdk.threads_init()

    win = SampleMaker()
    win.show()
    gtk.main()
